import { cloneDeep } from 'lodash';
import { OnGlobalStateChangeCallback, MicroAppStateActions } from './interfaces';
let globalState = {};
const deps = {};

function emitGlobal(state, prevState) {
  Object.keys(deps).forEach(id => {
    if (deps[id] instanceof Function) {
      deps[id](cloneDeep(state), cloneDeep(prevState));
    }
  });
}
/**
 * 定义全局状态，并返回通信方法，一般由主应用调用，微应用通过 props 获取通信方法。 
 * @param state 全局状态，{ key: value }
 */
export function initGlobalState(state = {}) {
  if (state === globalState) {
    console.warn('[qiankun] state has not changed！');
  } else {
    // 方法有可能被重复调用，将已有的全局状态克隆一份，为空则是第一次调用 initGlobalState 方法，不为空则非第一次次调用
    const prevGlobalState = cloneDeep(globalState);
    // 将传递的状态克隆一份赋值为 globalState
    globalState = cloneDeep(state);
    // 触发全局监听，当然在这个位置调用，正常情况下没啥反应，因为现在还没有应用注册回调函数
    emitGlobal(globalState, prevGlobalState);
  }
  // 返回通信方法，参数表示应用 id，true 表示自己是主应用调用
  return getMicroAppStateActions(`global-${+new Date()}`, true);
}
export function getMicroAppStateActions(id, isMaster) {
  return {
    onGlobalStateChange(callback, fireImmediately) {
      if (!(callback instanceof Function)) {
        console.error('[qiankun] callback must be function!');
        return;
      }

      if (deps[id]) {
        console.warn(`[qiankun] '${id}' global listener already exists before this, new listener will overwrite it.`);
      }

      deps[id] = callback;
      const cloneState = cloneDeep(globalState);

      if (fireImmediately) {
        callback(cloneState, cloneState);
      }
    },

    setGlobalState(state = {}) {
      if (state === globalState) {
        console.warn('[qiankun] state has not changed！');
        return false;
      }

      const changeKeys = [];
      const prevGlobalState = cloneDeep(globalState);
      globalState = cloneDeep(Object.keys(state).reduce((_globalState, changeKey) => {
        if (isMaster || _globalState.hasOwnProperty(changeKey)) {
          changeKeys.push(changeKey);
          return Object.assign(_globalState, {
            [changeKey]: state[changeKey]
          });
        }

        console.warn(`[qiankun] '${changeKey}' not declared when init state！`);
        return _globalState;
      }, globalState));

      if (changeKeys.length === 0) {
        console.warn('[qiankun] state has not changed！');
        return false;
      }

      emitGlobal(globalState, prevGlobalState);
      return true;
    },

    offGlobalStateChange() {
      delete deps[id];
      return true;
    }

  };
}